#include "gb28181_invite.h"
#include "../gb28181_service.h"
#include "../gb28181_utils.h"
#include "utils_log.h"
#include <sstream>
#include <regex>
#include <random>
#include <chrono>
#include <iomanip>
#include <cstring>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>

namespace El {
namespace GB28181 {

void GB28181Invite::ProcessInviteRequest(eXosip_event_t *event, eXosip_t *exosip_context,
                                       const SipServerConfig &config,
                                       MediaSessionCallback sessionCallback)
{
    if (!event || !event->request || !exosip_context) {
        EL_ERROR("Invalid parameters in ProcessInviteRequest");
        return;
    }

    osip_message_t *request = event->request;
    EL_INFO("Processing INVITE request for device {}", config.device_id);

    // Extract channel ID from request URI
    std::string channel_id = ExtractChannelId(request);
    if (channel_id.empty()) {
        EL_ERROR("Failed to extract channel ID from INVITE request");
        eXosip_call_send_answer(exosip_context, event->tid, 400, nullptr);
        return;
    }

    // Check if channel exists in config
    bool channel_found = false;
    for (const auto& ch : config.channels) {
        if (ch == channel_id) {
            channel_found = true;
            break;
        }
    }
    
    if (!channel_found) {
        EL_ERROR("Channel {} not found in device configuration", channel_id);
        eXosip_call_send_answer(exosip_context, event->tid, 404, nullptr);
        return;
    }

    // Extract SDP content
    std::string sdp_content;
    osip_body_t *body = nullptr;
    osip_message_get_body(request, 0, &body);
    if (body && body->body) {
        sdp_content = std::string(body->body);
    }

    if (sdp_content.empty()) {
        EL_ERROR("No SDP content found in INVITE request");
        eXosip_call_send_answer(exosip_context, event->tid, 400, nullptr);
        return;
    }

    // Parse SDP to extract media session info
    MediaSession session;
    session.channel_id = channel_id;
    session.session_id = std::to_string(event->cid);
    session.start_time = std::chrono::steady_clock::now();
    
    if (!ParseInviteSDP(sdp_content, session)) {
        EL_ERROR("Failed to parse SDP content");
        eXosip_call_send_answer(exosip_context, event->tid, 400, nullptr);
        return;
    }

    // Find available local port for RTP
    session.local_port = FindAvailablePort();
    if (session.local_port == 0) {
        EL_ERROR("Failed to find available port for RTP");
        eXosip_call_send_answer(exosip_context, event->tid, 500, nullptr);
        return;
    }

    // Get local IP
    session.local_ip = GetLocalIP();
    session.ssrc = GenerateSSRC(channel_id);

    EL_INFO("Created media session: channel={}, client={}:{}, local={}:{}, ssrc={}",
             session.channel_id, session.client_ip, session.client_port,
             session.local_ip, session.local_port, session.ssrc);

    // Send 200 OK with SDP response
    SendInviteOk(exosip_context, event, session);

    // Notify callback about new media session
    if (sessionCallback) {
        sessionCallback(session, true);
    }
}

void GB28181Invite::ProcessByeRequest(eXosip_event_t *event, eXosip_t *exosip_context,
                                    const SipServerConfig &config,
                                    MediaSessionCallback sessionCallback)
{
    if (!event || !event->request || !exosip_context) {
        EL_ERROR("Invalid parameters in ProcessByeRequest");
        return;
    }

    EL_INFO("Processing BYE request for device {}", config.device_id);

    // Create media session info for cleanup
    MediaSession session;
    session.session_id = std::to_string(event->cid);
    
    // Send 200 OK response
    SendByeOk(exosip_context, event);

    // Notify callback about session end
    if (sessionCallback) {
        sessionCallback(session, false);
    }
}

void GB28181Invite::SendInviteOk(eXosip_t *exosip_context, eXosip_event_t *event,
                               const MediaSession &session)
{
    if (!exosip_context || !event) {
        EL_ERROR("Invalid parameters in SendInviteOk");
        return;
    }

    osip_message_t *response = nullptr;
    eXosip_call_build_answer(exosip_context, event->tid, 200, &response);

    if (response) {
        // Create simple SDP response without config dependency
        std::string sdp_response = CreateResponseSDP(session);
        
        // Set SDP content
        osip_message_set_content_type(response, "application/sdp");
        osip_message_set_body(response, sdp_response.c_str(), sdp_response.length());
        
        // Send response
        eXosip_call_send_answer(exosip_context, event->tid, 200, response);
        
        EL_INFO("Sent 200 OK response for INVITE with SDP");
    } else {
        EL_ERROR("Failed to build INVITE OK response");
    }
}

void GB28181Invite::SendByeOk(eXosip_t *exosip_context, eXosip_event_t *event)
{
    if (!exosip_context || !event) {
        EL_ERROR("Invalid parameters in SendByeOk");
        return;
    }

    osip_message_t *response = nullptr;
    eXosip_call_build_answer(exosip_context, event->tid, 200, &response);

    if (response) {
        eXosip_call_send_answer(exosip_context, event->tid, 200, response);
        EL_INFO("Sent 200 OK response for BYE");
    } else {
        EL_ERROR("Failed to build BYE OK response");
    }
}

bool GB28181Invite::ParseInviteSDP(const std::string &sdp_content, MediaSession &session)
{
    std::istringstream iss(sdp_content);
    std::string line;
    
    while (std::getline(iss, line)) {
        // Remove carriage return if present
        if (!line.empty() && line.back() == '\r') {
            line.pop_back();
        }
        
        if (line.empty()) continue;
        
        // Parse connection line (c=IN IP4 192.168.1.100)
        if (line.substr(0, 2) == "c=") {
            std::string network_type, address_type, address;
            if (ParseSDPConnection(line, network_type, address_type, address)) {
                session.client_ip = address;
            }
        }
        // Parse media line (m=video 9002 RTP/AVP 96)
        else if (line.substr(0, 2) == "m=") {
            std::string media_type, protocol;
            int port;
            if (ParseSDPMedia(line, media_type, port, protocol)) {
                session.client_port = port;
                session.is_tcp = (protocol.find("TCP") != std::string::npos);
            }
        }
        // Parse y attribute for SSRC (y=0123456789)
        else if (line.substr(0, 2) == "y=") {
            session.ssrc = line.substr(2);
        }
    }
    
    // Validate parsed data
    if (session.client_ip.empty() || session.client_port == 0) {
        EL_ERROR("Failed to parse required SDP fields: ip={}, port={}", 
                 session.client_ip, session.client_port);
        return false;
    }
    
    return true;
}

std::string GB28181Invite::CreateResponseSDP(const MediaSession &session)
{
    std::ostringstream sdp;
    
    // Version
    sdp << "v=0\r\n";
    
    // Origin (use generic device ID)
    sdp << "o=GB28181 0 0 IN IP4 " << session.local_ip << "\r\n";
    
    // Session name
    sdp << "s=Play\r\n";
    
    // Connection info
    sdp << "c=IN IP4 " << session.local_ip << "\r\n";
    
    // Time
    sdp << "t=0 0\r\n";
    
    // Media description for video
    sdp << "m=video " << session.local_port << " RTP/AVP 96\r\n";
    
    // Media attributes
    sdp << "a=rtpmap:96 PS/90000\r\n";
    sdp << "a=sendonly\r\n";
    
    // SSRC attribute for GB28181
    sdp << "y=" << session.ssrc << "\r\n";
    
    return sdp.str();
}

std::string GB28181Invite::ExtractChannelId(osip_message_t *message)
{
    if (!message || !message->req_uri) {
        return "";
    }
    
    std::string uri_string = message->req_uri->string;
    
    // Parse URI like "sip:34020000001310000001@192.168.1.100:5060"
    std::regex uri_regex(R"(sip:([^@]+)@)");
    std::smatch matches;
    
    if (std::regex_search(uri_string, matches, uri_regex)) {
        return matches[1].str();
    }
    
    return "";
}

std::string GB28181Invite::GenerateSSRC(const std::string &channel_id)
{
    // Use channel_id hash as base for SSRC
    std::hash<std::string> hasher;
    uint32_t hash_value = static_cast<uint32_t>(hasher(channel_id));
    
    // Ensure it's 10 digits
    std::ostringstream oss;
    oss << std::setfill('0') << std::setw(10) << (hash_value % 10000000000ULL);
    
    return oss.str();
}

int GB28181Invite::FindAvailablePort(int start_port)
{
    for (int port = start_port; port < start_port + 1000; port += 2) { // Use even ports for RTP
        int sock = socket(AF_INET, SOCK_DGRAM, 0);
        if (sock < 0) continue;
        
        struct sockaddr_in addr;
        memset(&addr, 0, sizeof(addr));
        addr.sin_family = AF_INET;
        addr.sin_addr.s_addr = INADDR_ANY;
        addr.sin_port = htons(port);
        
        if (bind(sock, (struct sockaddr*)&addr, sizeof(addr)) == 0) {
            close(sock);
            return port;
        }
        
        close(sock);
    }
    
    return 0; // No available port found
}

std::string GB28181Invite::GetLocalIP()
{
    // Simple implementation - in production, this should detect the correct interface
    int sock = socket(AF_INET, SOCK_DGRAM, 0);
    if (sock < 0) return "127.0.0.1";
    
    struct sockaddr_in serv;
    memset(&serv, 0, sizeof(serv));
    serv.sin_family = AF_INET;
    serv.sin_addr.s_addr = inet_addr("8.8.8.8");
    serv.sin_port = htons(53);
    
    if (connect(sock, (const struct sockaddr*)&serv, sizeof(serv)) != 0) {
        close(sock);
        return "127.0.0.1";
    }
    
    struct sockaddr_in name;
    socklen_t namelen = sizeof(name);
    if (getsockname(sock, (struct sockaddr*)&name, &namelen) != 0) {
        close(sock);
        return "127.0.0.1";
    }
    
    close(sock);
    return std::string(inet_ntoa(name.sin_addr));
}

bool GB28181Invite::ParseSDPAttribute(const std::string &line, const std::string &attr, std::string &value)
{
    if (line.size() < attr.size() + 3) return false; // "a=" + attr + "="
    
    if (line.substr(0, 2) != "a=") return false;
    if (line.substr(2, attr.size()) != attr) return false;
    if (line[2 + attr.size()] != '=' && line[2 + attr.size()] != ':') return false;
    
    value = line.substr(2 + attr.size() + 1);
    return true;
}

bool GB28181Invite::ParseSDPMedia(const std::string &line, std::string &media_type, int &port, std::string &protocol)
{
    if (line.substr(0, 2) != "m=") return false;
    
    std::istringstream iss(line.substr(2));
    std::string port_str;
    
    if (!(iss >> media_type >> port_str >> protocol)) {
        return false;
    }
    
    try {
        port = std::stoi(port_str);
    } catch (const std::exception&) {
        return false;
    }
    
    return true;
}

bool GB28181Invite::ParseSDPConnection(const std::string &line, std::string &network_type, 
                                     std::string &address_type, std::string &address)
{
    if (line.substr(0, 2) != "c=") return false;
    
    std::istringstream iss(line.substr(2));
    
    if (!(iss >> network_type >> address_type >> address)) {
        return false;
    }
    
    return true;
}

} // namespace GB28181
} // namespace El